Header file bounded_type.hpp

namespace type_safe
{
    namespace constraints
    {
        struct dynamic_bound;
        
        template <typename T, typename Bound = dynamic_bound>
        class less;
        
        template <typename T, typename Bound = dynamic_bound>
        class less_equal;
        
        template <typename T, typename Bound = dynamic_bound>
        class greater;
        
        template <typename T, typename Bound = dynamic_bound>
        class greater_equal;
        
        //=== Open/Closed Tags ===//
        constexpr bool open = false;
        constexpr bool closed = true;
        
        template <typename T, bool LowerInclusive, bool UpperInclusive, typename LowerBound = dynamic_bound, typename UpperBound = dynamic_bound>
        class bounded;
        
        template <typename T, typename LowerBound = dynamic_bound, typename UpperBound = dynamic_bound>
        using open_interval = bounded<T, open, open, LowerBound, UpperBound>;
        
        template <typename T, typename LowerBound = dynamic_bound, typename UpperBound = dynamic_bound>
        using closed_interval = bounded<T, closed, closed, LowerBound, UpperBound>;
    }
    
    template <typename T, bool LowerInclusive, bool UpperInclusive, typename LowerBound = constraints::dynamic_bound, typename UpperBound = constraints::dynamic_bound, typename Verifier = assertion_verifier>
    using bounded_type = constrained_type<T, constraints::bounded<T, LowerInclusive, UpperInclusive, LowerBound, UpperBound>, Verifier>;
    
    inline namespace literals
    {
        template <char ... Digits>
        constexpr 'hidden' operator""_bound();
        template <char ... Digits>
        constexpr 'hidden' operator""_boundu();
    }
    
    template <typename T, typename U1, typename U2>
    constexpr 'hidden' make_bounded(T&& value, U1&& lower, U2&& upper);
    
    template <typename T, typename U1, typename U2>
    constexpr 'hidden' sanitize_bounded(T&& value, U1&& lower, U2&& upper);
    
    template <typename T, typename U1, typename U2>
    constexpr 'hidden' make_bounded_exclusive(T&& value, U1&& lower, U2&& upper);
    
    template <typename T, typename U1, typename U2>
    constexpr 'hidden' sanitize_bounded_exclusive(T&& value, U1&& lower, U2&& upper);
    
    //=== clamped_type ===//
    template <typename T, typename LowerBound, typename UpperBound, typename U>
    constexpr typename std::decay<U>::type clamp(const constraints::closed_interval<T, LowerBound, UpperBound>& interval, U&& val);
    
    struct clamping_verifier;
    
    template <typename T, typename LowerBound = constraints::dynamic_bound, typename UpperBound = constraints::dynamic_bound>
    using clamped_type = constrained_type<T, constraints::closed_interval<T, LowerBound, UpperBound>, clamping_verifier>;
    
    template <typename T, typename U1, typename U2>
    constexpr 'hidden' make_clamped(T&& value, U1&& lower, U2&& upper);
}

Struct type_safe::constraints::dynamic_bound

struct dynamic_bound
{
};

Tag type to enable a dynamic bound.

Class template type_safe::constraints::less

template <typename T, typename Bound = dynamic_bound>
class less
{
public:
    using value_type = decltype(base::value);
    
    using bound_type = Bound;
    
    constexpr less(Bound = {});
    
    constexpr less(const T& bound);
    constexpr less(T&& bound) noexcept('hidden');
    
    template <typename U>
    constexpr bool operator()(const U& u) const;
    
    constexpr const value_type& get_bound() const noexcept;
};

A Constraint for the ts::constrained_type.

A value is valid if it is less than some given value.

Default constructor type_safe::constraints::less::less::less

constexpr less(Bound = {});

Initializes it with a static bound.

Effects: Does nothing, a static bound is not stored. It will use Bound::value as the bound.

Notes: This constructor only participates in overload resolution, if a static bound is used, i.e. Bound is not ts::constraints::dynamic_bound.

Constructor type_safe::constraints::less::less::less

(1)  constexpr less(const T& bound);

(2)  constexpr less(T&& bound) noexcept('hidden');

Initializes it with a dynamic bound.

Effects: Copies (1)/moves (2) the object and uses that as bound.

Notes: These constructors only participate in overload resolution, if a dynamic bound is used, i.e. Bound is ts::constraints::dynamic_bound.

Function call operator type_safe::constraints::less::operator()

template <typename U>
constexpr bool operator()(const U& u) const;

Does the actual bounds check.

Function type_safe::constraints::less::get_bound

constexpr const value_type& get_bound() const noexcept;

Returns: The bound.


Class template type_safe::constraints::less_equal

template <typename T, typename Bound = dynamic_bound>
class less_equal
{
public:
    using value_type = decltype(base::value);
    
    using bound_type = Bound;
    
    constexpr less_equal(Bound = {});
    
    constexpr less_equal(const T& bound);
    constexpr less_equal(T&& bound) noexcept('hidden');
    
    template <typename U>
    constexpr bool operator()(const U& u) const;
    
    constexpr const value_type& get_bound() const noexcept;
};

A Constraint for the ts::constrained_type.

A value is valid if it is less than or equal to some given value.

Default constructor type_safe::constraints::less_equal::less_equal::less_equal

constexpr less_equal(Bound = {});

Initializes it with a static bound.

Effects: Does nothing, a static bound is not stored. It will use Bound::value as the bound.

Notes: This constructor only participates in overload resolution, if a static bound is used, i.e. Bound is not ts::constraints::dynamic_bound.

Constructor type_safe::constraints::less_equal::less_equal::less_equal

(1)  constexpr less_equal(const T& bound);

(2)  constexpr less_equal(T&& bound) noexcept('hidden');

Initializes it with a dynamic bound.

Effects: Copies (1)/moves (2) the object and uses that as bound.

Notes: These constructors only participate in overload resolution, if a dynamic bound is used, i.e. Bound is ts::constraints::dynamic_bound.

Function call operator type_safe::constraints::less_equal::operator()

template <typename U>
constexpr bool operator()(const U& u) const;

Does the actual bounds check.

Function type_safe::constraints::less_equal::get_bound

constexpr const value_type& get_bound() const noexcept;

Returns: The bound.


Class template type_safe::constraints::greater

template <typename T, typename Bound = dynamic_bound>
class greater
{
public:
    using value_type = decltype(base::value);
    
    using bound_type = Bound;
    
    constexpr greater(Bound = {});
    
    constexpr greater(const T& bound);
    constexpr greater(T&& bound) noexcept('hidden');
    
    template <typename U>
    constexpr bool operator()(const U& u) const;
    
    constexpr const value_type& get_bound() const noexcept;
};

A Constraint for the ts::constrained_type.

A value is valid if it is greater than some given value.

Default constructor type_safe::constraints::greater::greater::greater

constexpr greater(Bound = {});

Initializes it with a static bound.

Effects: Does nothing, a static bound is not stored. It will use Bound::value as the bound.

Notes: This constructor only participates in overload resolution, if a static bound is used, i.e. Bound is not ts::constraints::dynamic_bound.

Constructor type_safe::constraints::greater::greater::greater

(1)  constexpr greater(const T& bound);

(2)  constexpr greater(T&& bound) noexcept('hidden');

Initializes it with a dynamic bound.

Effects: Copies (1)/moves (2) the object and uses that as bound.

Notes: These constructors only participate in overload resolution, if a dynamic bound is used, i.e. Bound is ts::constraints::dynamic_bound.

Function call operator type_safe::constraints::greater::operator()

template <typename U>
constexpr bool operator()(const U& u) const;

Does the actual bounds check.

Function type_safe::constraints::greater::get_bound

constexpr const value_type& get_bound() const noexcept;

Returns: The bound.


Class template type_safe::constraints::greater_equal

template <typename T, typename Bound = dynamic_bound>
class greater_equal
{
public:
    using value_type = decltype(base::value);
    
    using bound_type = Bound;
    
    constexpr greater_equal(Bound = {});
    
    constexpr greater_equal(const T& bound);
    constexpr greater_equal(T&& bound) noexcept('hidden');
    
    template <typename U>
    constexpr bool operator()(const U& u) const;
    
    constexpr const value_type& get_bound() const noexcept;
};

A Constraint for the ts::constrained_type.

A value is valid if it is greater than or equal to some given value.

Default constructor type_safe::constraints::greater_equal::greater_equal::greater_equal

constexpr greater_equal(Bound = {});

Initializes it with a static bound.

Effects: Does nothing, a static bound is not stored. It will use Bound::value as the bound.

Notes: This constructor only participates in overload resolution, if a static bound is used, i.e. Bound is not ts::constraints::dynamic_bound.

Constructor type_safe::constraints::greater_equal::greater_equal::greater_equal

(1)  constexpr greater_equal(const T& bound);

(2)  constexpr greater_equal(T&& bound) noexcept('hidden');

Initializes it with a dynamic bound.

Effects: Copies (1)/moves (2) the object and uses that as bound.

Notes: These constructors only participate in overload resolution, if a dynamic bound is used, i.e. Bound is ts::constraints::dynamic_bound.

Function call operator type_safe::constraints::greater_equal::operator()

template <typename U>
constexpr bool operator()(const U& u) const;

Does the actual bounds check.

Function type_safe::constraints::greater_equal::get_bound

constexpr const value_type& get_bound() const noexcept;

Returns: The bound.


Open/Closed Tags

(1)  constexpr bool open = false;

(2)  constexpr bool closed = true;

Tag objects to specify bounds for ts::constraints::bounded.

Class template type_safe::constraints::bounded

template <typename T, bool LowerInclusive, bool UpperInclusive, typename LowerBound = dynamic_bound, typename UpperBound = dynamic_bound>
class bounded
{
public:
    using value_type = T;
    
    using lower_bound = LowerBound;
    
    using upper_bound = UpperBound;
    
    static constexpr auto lower_inclusive = LowerInclusive;
    
    static constexpr auto upper_inclusive = UpperInclusive;
    
    constexpr bounded();
    
    template <typename U1, typename U2>
    constexpr bounded(U1&& lower, U2&& upper, decltype(lower_type(std::forward<U1>(lower)), 0) = 0, decltype(upper_type(std::forward<U2>(upper)), 0) = 0);
    
    template <typename U>
    constexpr bool operator()(const U& u) const;
    
    constexpr const typename lower_type::value_type& get_lower_bound() const noexcept;
    
    constexpr const typename upper_type::value_type& get_upper_bound() const noexcept;
};

A Constraint for the ts::constrained_type.

A value is valid if it is between two given bounds, LowerInclusive/UpperInclusive control whether the lower/upper bound itself is valid too.

Default constructor type_safe::constraints::bounded::bounded::bounded

constexpr bounded();

Initializes it with static bounds.

Effects: Does nothing, a static bound is not stored. It will use LowerBound::value as lower bound and UpperBound::value as upper bound.

Notes: This constructor does not participate in overload resolution, unless both bounds are static, i.e. not ts::constraints::dynamic_bound.

Function template type_safe::constraints::bounded::bounded

template <typename U1, typename U2>
constexpr bounded(U1&& lower, U2&& upper, decltype(lower_type(std::forward<U1>(lower)), 0) = 0, decltype(upper_type(std::forward<U2>(upper)), 0) = 0);

Initializes it with (mixed) dynamic bounds.

Effects: Perfectly forwards the arguments to the bounds. If a bound is static, the static member value will be used as bound, if it is dynamic, a copy created by perfectly forwarding will be stored and used as bound. \notes This constructor does not participate in overload resolution, unless the arguments are convertible to the bounds. \param 2 \exclude \param 3 \exclude

Function call operator type_safe::constraints::bounded::operator()

template <typename U>
constexpr bool operator()(const U& u) const;

Does the bounds check.

Function type_safe::constraints::bounded::get_lower_bound

constexpr const typename lower_type::value_type& get_lower_bound() const noexcept;

Returns: The value of the lower bound.

Function type_safe::constraints::bounded::get_upper_bound

constexpr const typename upper_type::value_type& get_upper_bound() const noexcept;

Returns: The value of the upper bound.


Alias template type_safe::constraints::open_interval

template <typename T, typename LowerBound = dynamic_bound, typename UpperBound = dynamic_bound>
using open_interval = bounded<T, open, open, LowerBound, UpperBound>;

A Constraint for the ts::constrained_type.

A value is valid if it is between two given bounds but not the bounds themselves.

Alias template type_safe::constraints::closed_interval

template <typename T, typename LowerBound = dynamic_bound, typename UpperBound = dynamic_bound>
using closed_interval = bounded<T, closed, closed, LowerBound, UpperBound>;

A Constraint for the ts::constrained_type.

A value is valid if it is between two given bounds or the bounds themselves.


Alias template type_safe::bounded_type

template <typename T, bool LowerInclusive, bool UpperInclusive, typename LowerBound = constraints::dynamic_bound, typename UpperBound = constraints::dynamic_bound, typename Verifier = assertion_verifier>
using bounded_type = constrained_type<T, constraints::bounded<T, LowerInclusive, UpperInclusive, LowerBound, UpperBound>, Verifier>;

An alias for ts::constrained_type that uses ts::constraints::bounded as its Constraint. \notes This is some type where the values must be in a certain interval.

User defined literal type_safe::literals::operator""_bound

(1)  template <char ... Digits>
     constexpr 'hidden' operator""_bound();

(2)  template <char ... Digits>
     constexpr 'hidden' operator""_boundu();

Creates a static bound for ts::bounded_type.

This is a bound encapsulated in the type, so there is no overhead. You can use it for example like this ts::make_bounded(50, 0_bound, 100_bound), to bound an integer between 0 and 100.

Returns: A type representing the given value, the value has type long long (1)/unsigned long long(2).


Function template type_safe::make_bounded

template <typename T, typename U1, typename U2>
constexpr 'hidden' make_bounded(T&& value, U1&& lower, U2&& upper);

Creates a ts::bounded_type to a specified ts::constraints::closed_interval.

Returns: A ts::bounded_type with the given value and lower and upper bounds, where the bounds are valid values as well.

Requires: As it uses ts::assertion_verifier, the value must be valid.

Notes: If this function is passed in dynamic values of the same type as value, it will create a dynamic bound. Otherwise it must be passed static bounds.

Function template type_safe::sanitize_bounded

template <typename T, typename U1, typename U2>
constexpr 'hidden' sanitize_bounded(T&& value, U1&& lower, U2&& upper);

Creates a ts::bounded_type to a specified ts::constraints::closed_interval.

Returns: A ts::bounded_type with the given value and lower and upper bounds, where the bounds are valid values as well.

Throws: A ts::constrain_error if the value isn't valid, or anything else thrown by the constructor.

Notes: This is meant for sanitizing user input, using a recoverable error handling strategy.

Notes: If this function is passed in dynamic values of the same type as value, it will create a dynamic bound. Otherwise it must be passed static bounds.

Function template type_safe::make_bounded_exclusive

template <typename T, typename U1, typename U2>
constexpr 'hidden' make_bounded_exclusive(T&& value, U1&& lower, U2&& upper);

Creates a ts::bounded_type to a specified ts::constraints::open_interval.

Returns: A ts::bounded_type with the given value and lower and upper bounds, where the bounds are not valid values.

Requires: As it uses ts::assertion_verifier, the value must be valid.

Notes: If this function is passed in dynamic values of the same type as value, it will create a dynamic bound. Otherwise it must be passed static bounds.

Function template type_safe::sanitize_bounded_exclusive

template <typename T, typename U1, typename U2>
constexpr 'hidden' sanitize_bounded_exclusive(T&& value, U1&& lower, U2&& upper);

Creates a ts::bounded_type to a specified ts::constraints::open_interval, using ts::throwing_verifier.

Returns: A ts::bounded_type with the given value and lower and upper bounds, where the bounds are not valid values.

Throws: A ts::constrain_error if the value isn't valid, or anything else thrown by the constructor.

Notes: This is meant for sanitizing user input, using a recoverable error handling strategy.

Notes: If this function is passed in dynamic values of the same type as value, it will create a dynamic bound. Otherwise it must be passed static bounds.

Function template type_safe::clamp

template <typename T, typename LowerBound, typename UpperBound, typename U>
constexpr typename std::decay<U>::type clamp(const constraints::closed_interval<T, LowerBound, UpperBound>& interval, U&& val);

Returns a copy of val so that it is in the given ts::constraints::closed_interval.

Effects: If it is not in the interval, returns the bound that is closer to the value.

Struct type_safe::clamping_verifier

struct clamping_verifier
{
    template <typename Value, typename T, typename Bound>
    static constexpr typename std::decay<Value>::type verify(Value&& val, const constraints::less_equal<T, Bound>& p);
    
    template <typename Value, typename T, typename Bound>
    static constexpr typename std::decay<Value>::type verify(Value&& val, const constraints::greater_equal<T, Bound>& p);
    
    template <typename Value, typename T, typename LowerBound, typename UpperBound>
    static constexpr typename std::decay<Value>::type verify(Value&& val, const constraints::closed_interval<T, LowerBound, UpperBound>& interval);
};

A Verifier for ts::constrained_type that clamps the value to make it valid.

It must be used together with ts::constraints::less_equal, ts::constraints::greater_equal or ts::constraints::closed_interval.

Function template type_safe::clamping_verifier::verify

template <typename Value, typename T, typename Bound>
static constexpr typename std::decay<Value>::type verify(Value&& val, const constraints::less_equal<T, Bound>& p);

Returns: If val is greater than the bound of p, returns the bound. Otherwise returns the value unchanged

Function template type_safe::clamping_verifier::verify

template <typename Value, typename T, typename Bound>
static constexpr typename std::decay<Value>::type verify(Value&& val, const constraints::greater_equal<T, Bound>& p);

Returns: If val is less than the bound of p, returns the bound. Otherwise returns the value unchanged

Function template type_safe::clamping_verifier::verify

template <typename Value, typename T, typename LowerBound, typename UpperBound>
static constexpr typename std::decay<Value>::type verify(Value&& val, const constraints::closed_interval<T, LowerBound, UpperBound>& interval);

Returns: Same as clamp(interval, val).


Alias template type_safe::clamped_type

template <typename T, typename LowerBound = constraints::dynamic_bound, typename UpperBound = constraints::dynamic_bound>
using clamped_type = constrained_type<T, constraints::closed_interval<T, LowerBound, UpperBound>, clamping_verifier>;

An alias for ts::constrained_type that uses ts::constraints::closed_interval as its Constraint and ts::clamping_verifier as its Verifier. \notes This is some type where the values are always clamped so that they are in a certain interval.

Function template type_safe::make_clamped

template <typename T, typename U1, typename U2>
constexpr 'hidden' make_clamped(T&& value, U1&& lower, U2&& upper);

Creates a ts::clamped_type from the specified ts::constraints::closed_interval.

Returns: A ts::clamped_type with the given value and lower and upper bounds, where the bounds are valid values.

Notes: If this function is passed in dynamic values of the same type as value, it will create a dynamic bound. Otherwise it must be passed static bounds.